<!--文件上传-->
<template>
	<a-form-item-rest>
		<div class="file-upload-container">
			<a-image-preview-group v-if="multiple || onlyShowPreview">
				<draggable v-if="files.length" class="file-preview" v-bind="dragOptions">
					<template #item="{element: file}">
						<div>
							<a v-if="!FILE_ICON_MAP.image.regex.test(file.url)" :href="file.url" class="other-accessories">{{ file.fileOriginName }}</a>
							<template v-else>
								<a-image v-if="file.id" :src="file.url" v-bind="previewOptions" @error="imgLoadError(file)">
									<template #placeholder>
										<a-image :src="`${file.url}${ossImageStyle}`" :preview="false" />
									</template>
									<template v-if="!onlyShowPreview" #previewMask>
										<a-select v-if="subTypes && subTypes.length" v-model:value="file.subType" :options="subTypes" size="small" :bordered="false" @change="changeSubType(file)" @click.stop />
										<a class="remove-btn" @click.stop="removeFile(file)">
											<close-outlined />
										</a>
									</template>
								</a-image>
								<a-progress v-else type="circle" :percent="file.percent" />
							</template>
						</div>
					</template>
				</draggable>
			</a-image-preview-group>
			<div v-if="!onlyShowPreview" class="file-upload-wrapper">
				<a-upload v-model:file-list="files" v-bind="options" class="file-upload">
					<template v-if="multiple || files.length === 0">
						<slot name="icon">
							<svg-icon icon-class="image" />
						</slot>
						<slot name="tip">添加图片</slot>
						<div v-if="subTypes && subTypes.length" @click.self.stop class="upload-type-btns">
							<a-button v-for="type in subTypes" :key="type.value" size="small" ghost @click="params.subType = type.value">
								{{ type.label }}
							</a-button>
						</div>
					</template>
					<template v-else v-for="img in files" :key="img.id">
						<img v-if="img.url" :src="img.url" class="single-preview-img" />
						<a-progress v-else type="circle" :width="100" :percent="img.percent" />
						<span>{{ img.configName }}</span>
						<a class="remove-btn" @click.stop="removeFile(img)">
							<close-outlined />
						</a>
					</template>
				</a-upload>
				<template v-if="showQrCode || subType">
					<span class="vertical-text">或扫码上传</span>
					<div class="qrcode-upload">
						<a-tooltip destroy-tooltip-on-hide color="white">
							<template #title>
								<qrcode-vue :value="qrcodeURL" :size="220" render-as="svg" />
							</template>
							<qrcode-vue :value="qrcodeURL" :size="110" render-as="svg" />
						</a-tooltip>
					</div>
				</template>
				<a-typography-paragraph v-if="showTips || subType" type="secondary" class="upload-tips">
					<p>温馨提示：</p>
					<br />
					<br />
					<p>* 仅支持图片格式（例如：jpg、png、jpeg等），{{ limit ? `最多上传${limit}张` : '' }}每张最大5M；</p>
					<p v-if="subType">* 拖动图片可以进行排序，第一张图片将作为封面图展示；</p>
					<p>* 上传图片后，{{ subTypes && subTypes.length ? '左上角可修改图片标签类型，' : '' }}鼠标放置图片上后右上角出现删除图标。</p>
				</a-typography-paragraph>
			</div>
		</div>
	</a-form-item-rest>
</template>

<script lang="ts" setup>
	import {computed, onMounted, onUnmounted, reactive, ref, toRef, watchEffect} from 'vue'
	import {authHeader, uploadPicURL} from 'yuxin-base/src/utils/axios'
	import {message, Upload} from 'ant-design-vue'
	import QrcodeVue from 'qrcode.vue'
	// @ts-ignore
	import Snowflake from 'vue-snowflake'
	import Draggable from 'vuedraggable-es'
	import {CloseOutlined} from '@ant-design/icons-vue'
	import fileApi from 'yuxin-base/src/api/file-api'
	import {getFileType} from 'yuxin-base/src/utils'
	import {useStore} from '@/store'
	import XEUtils from 'xe-utils'
	import {Img} from 'yuxin-base/src/types'
	import {SUB_TYPE_MAP} from 'yuxin-base/src/constant'
	import {EMPTY_IMAGE, FILE_ICON_MAP} from 'yuxin-base/src/constant'

	const store = useStore()

	const props = withDefaults(
		defineProps<{
			// 新增业务数据时无需设置，业务数据新增后更新图片的bizId，修改时需要设置
			bizId?: string
			// 文件列表
			file?: Img
			files?: Img[]
			// 最大上传数量
			limit?: number
			// 可接受的文件类型
			accept?: string
			// 是否支持多张上传
			multiple?: boolean
			// 业务类型
			bizType?: number
			// 子业务类型，需在subTypeMap内定义
			subType?: string
			// 是否显示提示
			showTips?: boolean
			// 是否显示扫码上传
			showQrCode?: boolean
			// 仅做预览功能，可传入预览时的图片宽高
			onlyShowPreview?: boolean | any
			// 单张图片，或者文件大小/M
			fileSize?:number
		}>(),
		{
			multiple: true,
			accept: '.jpg,.png,.jpeg'
		}
	)

	const loading = ref<boolean>(false)
	const files = ref<Img[]>(props.files || [])
	const emits = defineEmits(['update:file', 'update:files'])

	// 雪花ID生成器
	const node = new Snowflake.init(1, 1, 0)

	/**
	 * 图片上传业务参数
	 */
	let params = reactive<any>({
		isScale: 0,
		subType: null,
		indexNum: 1,
		bizType: toRef(props, 'bizType'),
		// 业务数据新增时生成一个默认业务id，用于同步扫码上传
		bizId: props.bizId || node.createID()
	})

	let query = reactive({
		limit: props.limit,
		bizId: params.bizId,
		subType: props.subType,
		bizType: toRef(props, 'bizType'),
		tenantId: store.currentUser.authCode
	})

	// 扫码上传地址
	const qrcodeURL = location.origin + '/h5-upload?' + XEUtils.serialize(query)

	// 阿里云oss图片压缩参数
	const ossImageStyle = '?x-oss-process=image/auto-orient,1/resize,m_lfit,w_400/blur,r_5,s_5'

	const dragOptions = reactive({
		list: files,
		itemKey: 'id',
		animation: 300,
		disabled: Boolean(props.onlyShowPreview),
		onStart() {
			loading.value = true
		},
		onEnd() {
			files.value.forEach((file, i) => (file.indexNum = i))
			fileApi.updateBatch(files.value).finally(() => (loading.value = false))
		}
	})

	// 设置预览参数
	const onlyShowPreview = computed(() => {
		if (props.onlyShowPreview) {
			return {
				width: (props.onlyShowPreview.width || 100) + 'px',
				height: (props.onlyShowPreview.height || 60) + 'px'
			}
		} else {
			return false
		}
	})

	const previewOptions = reactive({
		preview: {
			maskClassName: onlyShowPreview.value ? '' : 'action'
		},
		fallback: EMPTY_IMAGE,
		style: onlyShowPreview.value || {
			width: '180px',
			height: '120px'
		}
	})

	const imgLoadError = (file: Img) => {
		file.url = EMPTY_IMAGE
	}

	// 获取子业务类型
	const subTypes = computed(() => SUB_TYPE_MAP[props.subType || ''])

	/**
	 * 图片上传配置
	 */
	const options = reactive({
		name: 'files',
		data() {
			for (const key in params) {
				if (params[key] === null || params[key] === undefined) {
					delete params[key]
				}
			}
			return params
		},
		action: uploadPicURL,
		headers: authHeader(),
		showUploadList: false,
		accept: toRef(props, 'accept'),
		multiple: toRef(props, 'multiple'),
		maxCount: toRef(props, 'limit', props.multiple ? undefined : 1),
		beforeUpload(file: File, fileList: File[]) {
			const fileType = getFileType(file)
			if (props.accept && props.accept.includes(fileType)) {
				params.indexNum = files.value.length + 1
			} else {
				message.error(`文件类型不合法，请上传以下类型文件【${props.accept}】`)
				return false
			}
			if(props.fileSize){
				if(file.size / 1024 / 1024>props.fileSize){
					 message.error(`上传图片大小${props.fileSize}M以内!`)
					 return false
				}
			}
			if (props.limit) {
				// 超出数量限制的图片不做上传
				const slice = fileList.slice(0, props.limit - files.value.length)
				return slice.includes(file) || Upload.LIST_IGNORE
			}


		},
		onChange({file, fileList}: any) {
			const {response, status, error, percent = 0} = file
			const index = fileList.findIndex((o: any) => o === file)

			file.percent = Math.floor(Math.abs(percent - 1))

			if (status === 'done') {
				file.percent = 100
				if (response.code === '200') {
					fileList[index] = response.data
					emits('update:file', response.data)
				} else {
					fileList.splice(index, 1)
					message.error(response.msg)
				}

				emits('update:files', fileList)
			} else if (status === 'error') {
				message.error(`上传失败：${error.status}`)
			}
		}
	})

	/**
	 * 获取文件列表
	 * @param bizId
	 */
	const getFiles = () => {
		fileApi.getPicList(params.bizId).then(({data = []}) => {
			files.value = props.bizType ? data.filter(i => i.bizType === props.bizType) : data
			emits('update:files', data)
			if (!props.multiple) {
				emits('update:file', data[0] || null)
			}
		})
	}

	watchEffect(() => {
		if (props.file && props.file.url) {
			files.value = [props.file]
		} else if (props.files) {
			files.value = props.files
		}
	})

	watchEffect(() => {
		if (props.bizId) {
			params.bizId = props.bizId
		}
	})

	const timer = ref()
	onMounted(() => {
		if (props.bizId) {
			getFiles()
		}

		if (props.subType || props.showQrCode) {
			// 5秒钟请求一次图片列表，同步扫码上传的图片
			timer.value = setInterval(() => {
				// 页面操作时不定时更新数据
				if (!loading.value && !files.value.some(file => !file.id)) {
					getFiles()
				}
			}, 5000)
		}
	})

	onUnmounted(() => {
		clearInterval(timer.value)
	})

	/**
	 * 更新文件业务子类型
	 * @param file
	 */
	const changeSubType = (file: Img) => {
		fileApi.updateFile(file).then(() => {
			emits('update:files', files.value)
			message.success('图片信息更新成功')
		})
	}

	/**
	 * 删除文件
	 * @param file
	 */
	const removeFile = (file: Img) => {
		const index = files.value.findIndex(o => o === file)

		if (!props.multiple) {
			emits('update:file', null)
		}

		if (file.id && index > -1) {
			fileApi.removeFile(file.id).then(() => {
				files.value.splice(index, 1)
				emits('update:files', files.value)
				message.success('图片删除成功')
			})
		} else {
			files.value.splice(index, 1)
		}
	}
</script>

<style lang="less" scoped>
	.file-upload-container {
		.file-preview {
			display: flex;
			flex-wrap: wrap;
			gap: 12px;
			margin-bottom: 12px;

			.other-accessories {
				display: inline-block;
				background-color: #eff1f7;
				width: 100px;
				height: 60px;
				line-height: 60px;
				white-space: nowrap;
				overflow: hidden;
				text-overflow: ellipsis;
				text-align: center;
			}

			.ant-progress {
				width: 180px;
				height: 120px;
				text-align: center;
			}

			:deep(.ant-image) {
				//width: 180px;
				//height: 120px;

				.ant-image-img {
					width: 100%;
					height: 100%;
					object-fit: contain;
					background: #eff1f7;
				}

				.ant-image-mask.action {
					opacity: 1;
					background: transparent;

					.ant-select {
						top: 0;
						left: 0;
						color: #fff;
						width: 80px;
						font-size: 12px;
						position: absolute;
						background: rgba(0, 0, 0, 0.5);
						border-bottom-right-radius: 2px;

						.ant-select-arrow {
							color: #fff;
						}
					}

					&:hover .remove-btn {
						opacity: 0.6;
					}

					.remove-btn {
						top: 0;
						right: 0;
						color: #fff;
						width: 18px;
						height: 18px;
						opacity: 0;
						line-height: 18px;
						position: absolute;
						text-align: center;
						background: @error-color;
						border-bottom-left-radius: 2px;

						&:hover {
							opacity: 0.8;
						}

						span[role='img'] {
							position: unset;
							font-size: 12px;
						}
					}
				}
			}
		}

		.file-upload-wrapper {
			gap: 12px;
			margin: 0;
			display: flex;
			font-size: 12px;
			align-items: center;

			.qrcode-upload {
				width: 120px;
				height: 120px;
				display: flex;
				align-items: center;
				justify-content: center;
				border-radius: 2px;
				color: @primary-color;
				border: 1px dashed @primary-color;
			}

			.upload-tips {
				margin: 0;

				p {
					margin: 0;
				}
			}
		}

		.file-upload {
			:deep(span.ant-upload) {
				width: 180px;
				height: 120px;
				cursor: pointer;
				display: flex;
				position: relative;
				background: #fafafa;
				border-radius: 2px;
				font-size: 12px;
				align-items: center;
				justify-content: center;
				flex-direction: column;
				color: @primary-color;
				border: 1px dashed @primary-color;

				svg.svg-icon {
					width: 46px;
					height: 34px;
					margin-bottom: 12px;
				}

				.upload-type-btns {
					top: 0;
					left: 0;
					width: 100%;
					height: 100%;
					opacity: 0;
					display: inline-grid;
					position: absolute;
					align-items: center;
					background: rgba(0, 0, 0, 0.6);
					transition: opacity 0.5s;
					justify-content: space-evenly;
					grid-template-columns: repeat(3, 45px);

					.ant-btn {
						padding: 0;
					}
				}

				&:hover .upload-type-btns {
					opacity: 1;
				}

				.remove-btn {
					top: -1px;
					right: -1px;
					color: #fff;
					width: 18px;
					height: 18px;
					opacity: 0;
					line-height: 18px;
					position: absolute;
					text-align: center;
					background: @error-color;
					border-radius: 2px;
				}

				&:hover .remove-btn {
					opacity: 0.8;
				}
			}

			.single-preview-img {
				width: 100%;
				height: 100%;
				object-fit: contain;
			}
		}
	}
</style>
